﻿#region Header
/*
 * name：ServiceFactory
 * author：Kain
 * date：11/29/2010 1:34:41 PM
 *********************************************
 * History:
 * $Header$
 * $History$
 */
#endregion

#region imported namespace
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Unity;
using System.IO;
using System.Reflection;
using System.Diagnostics;
using Cherry.Agile.Configuration;
using Cherry.Agile.Core.Instrumentation;
using Microsoft.Practices.Unity.InterceptionExtension;
#endregion

namespace Cherry.Agile.Core {
    /// <summary>
    /// 服务构建工厂
    /// </summary>
    public class ServiceFactory : IServiceFactory {

        private static Type __serviceAttributeType = typeof(System.ServiceModel.ServiceContractAttribute);

        private string _lifetime;
        private IUnityContainer contianer;
        static bool _loaded;
        static object _sync = new object();
        string path;

        static Lazy<string> defaultFactoryName = new Lazy<string>(() => {
 
                var q = AgileConfiguration.Current.Services.Cast<ServiceElement>();
                var item = q.FirstOrDefault(c => c.Assembly == "*");
                if (item != null)
                    return item.Factory;
                else
                    return string.Empty;
            });

        static string GetFactoryName<T>() {
            var q = AgileConfiguration.Current.Services.Cast<ServiceElement>();
            string name = typeof(T).Name;
            string assembly = typeof(T).Module.Name;
            assembly = assembly.Substring(0, assembly.Length - 4);

            var e = q.FirstOrDefault(c => c.Type == name && c.Assembly == assembly);
            if (e == null)
                e = q.FirstOrDefault(c => c.Assembly == assembly && (c.Type == "*" || string.IsNullOrEmpty(c.Type)));
            if (e != null)
                return e.Factory;
            else
                return defaultFactoryName.Value;
        }

        static IServiceFactory GetInstance<T>() {
            string factoryName = GetFactoryName<T>();
            if (string.IsNullOrEmpty(factoryName))
                return Container.Current.Resolve<IServiceFactory>();
            else
                return Container.Current.Resolve<IServiceFactory>(factoryName);
        }

        static ServiceFactory() {
            InitialFactories();
        }

        static void InitialFactories() {
            var items = AgileConfiguration.Current.ServiceFactories;
            var policy = new InterceptionBehavior<PolicyInjectionBehavior>();
            var intercptor = new Interceptor<TransparentProxyInterceptor>();

            foreach (ServiceFactoryElement item in items) {
                if (item.Name == "*" && Container.Current.IsRegistered<IServiceFactory>())
                    Trace.TraceWarning("ServiceFactory\tsevice factory " + item.Type + "has been ironed registed into container.");
                try {
                    var type = Type.GetType(item.Type, true);
                    if (item.Name != "*") {
                        Container.Current.RegisterType(typeof(IServiceFactory), type,
                            item.Name,
                            GetLifetimeManager(item.Lifetime),
                            new InjectionConstructor(item.Lifetime),policy,intercptor);
                    } else {
                        Container.Current.RegisterType(typeof(IServiceFactory),
                            type,
                            GetLifetimeManager(item.Lifetime),
                            new InjectionConstructor(item.Lifetime), policy, intercptor);
                    }
                } catch (Exception ex) {
                    throw new InvalidOperationException("regist serivce factory error,make sure configration is correct" + item.Type, ex); //) "注册服务工厂错误，请确认配置的类型是否正确:";
                }
            }
            if (!Container.Current.IsRegistered<IServiceFactory>())
                Container.Current.RegisterInstance<IServiceFactory>(new ServiceFactory(string.Empty),
                    new ContainerControlledLifetimeManager());

        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="lifetime"></param>
        public ServiceFactory(string lifetime) {
            _lifetime = lifetime;
            contianer = Container.Current;
        }

        void Initial() {
            if (contianer == null)
                throw new ObjectDisposedException("ServiceFactory");
            if (!_loaded) {
                lock (_sync) {
                    if (!_loaded) {
                        path = AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory;
                        var dir = new DirectoryInfo(path);
                        var files = dir.GetFiles("*.services.???");
                        foreach (var file in files)
                            LoadAssemblyFromFile(file);

                    }
                }
                _loaded = true;
            }
        }

        private void LoadAssemblyFromFile(FileInfo file) {
            if (file.Extension == ".dll" || file.Extension == ".exe") {
                try {
                    Trace.TraceInformation("ServiceFactory\tload assembly " + file.Name);
                    var types = Assembly.LoadFile(file.FullName).GetTypes().Where(c => c.IsClass && !c.IsAbstract && c.IsPublic);
                    foreach (var item in types) {
                        RegistType(item);
                    }
                } catch {
                    Trace.TraceError("ServiceFactory\tload assembly failed " + file.Name);
                }
            }
        }

        private static LifetimeManager GetLifetimeManager(string lifetime) {
            if (string.IsNullOrEmpty(lifetime))
                lifetime = "default";
            if (Container.Current.IsRegistered<LifetimeManager>(lifetime))
                return Container.Current.Resolve<LifetimeManager>(lifetime);
            else
                return new Microsoft.Practices.Unity.PerResolveLifetimeManager();
        }

        private void RegistType(Type type) {
            var interfaces = type.GetInterfaces();
            var q = interfaces.Where(c => ValidateServiceInterface(c));
            if (q.Count() == 0) {
                Trace.TraceWarning("ServiceFactory\tcoud not find any service contract in type of " + type.FullName);
                return;
            }

            foreach (var item in q) {
                if (!this.contianer.IsRegistered(item))
                    this.contianer.RegisterType(item, type, GetLifetimeManager(_lifetime));
            }
        }

        private static bool ValidateServiceInterface(Type type) {
            if (!type.IsInterface)
                return false;
            return type.GetCustomAttributes(__serviceAttributeType, false).Length > 0;
        }

        /// <summary>
        /// 获取类型T实例
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <returns>T 的实例</returns>
        public static T GetService<T>() where T : class {
#if DEBUG
            if (!ValidateServiceInterface(typeof(T)))
                throw new ArgumentException("服务接口必须有ServiceContractAttribute标注");
#endif
            //return GetInstance<T>().GetService<T>();
            return GetService<T>(null);
        }

#if License
        static RempLicense lic = new RempLicense();
#endif

        /// <summary>
        /// 根据服务在容器中的配置名称从服务容器中获取服务实例
        /// </summary>
        /// <typeparam name="T">服务类型</typeparam>
        /// <param name="name">服务的名称</param>
        /// <returns>T的实例</returns>
        public static T GetService<T>(string name) where T : class {
#if DEBUG
            if (!ValidateServiceInterface(typeof(T)))
                throw new ArgumentException("服务接口必须有ServiceContractAttribute标注");
#endif

#if License
            if (System.Diagnostics.Debugger.IsAttached)
                throw new NotImplementedException();
            if (System.Web.HttpContext.Current != null)
                lic.Validate(typeof(T));
#endif

            //var serviceName = string.IsNullOrEmpty(name) ? typeof(T).Name : name;
            return GetInstance<T>().GetService<T>(name);
        }

        /// <summary>
        /// 创建一个工厂
        /// </summary>
        /// <param name="name">工厂名称</param>
        /// <returns><see cref="IServiceFactory"/></returns>
        public static IServiceFactory CreateFactory(string name) {
            if (Container.Current.IsRegistered<IServiceFactory>(name))
                return Container.Current.Resolve<IServiceFactory>(name);
            else
                return null;
        }

        #region IServiceFactory Members

        T IServiceFactory.GetService<T>() {
            Initial();
            try {
                return this.contianer.Resolve<T>();
            } catch (ResolutionFailedException rf) {
                throw new InvalidOperationException("构建服务对象错误，请确认'" + typeof(T).FullName + "'对应的实现Dll是否已经Copy到当前应用程序的bin目录或者运行目录:" + path, rf);
            }

        }

        T IServiceFactory.GetService<T>(string name) {
            Initial();
            try {
                return this.contianer.Resolve<T>(name);
            } catch (ResolutionFailedException re) {
                throw new InvalidOperationException("构建服务对象错误，请确认名称为" + "name" + "的对象'" + typeof(T).FullName + "'对应的实现Dll是否已经Copy到当前应用程序的bin目录或者运行目录中，并且已经注册", re);
            }
        }

        #endregion

        void IDisposable.Dispose() {

        }

        /// <summary>
        /// 
        /// </summary>
        public string Name {
            get {
                return "Default";
            }
        }

        /// <summary>
        /// 销毁服务
        /// </summary>
        /// <param name="obj"></param>
        public static void DestroyService(IDisposable obj) {
            DestroyService(obj, null);
        }

        internal static void DestroyService(IDisposable obj, IClientInstrumentationProvider instrumentation) {
            if (obj == null)
                return;
            var client = obj as System.ServiceModel.IClientChannel;
            if (client != null && client.State == System.ServiceModel.CommunicationState.Faulted) {
                client.Abort();
                if (instrumentation != null)
                    instrumentation.Faulted();
            } else {
                if (instrumentation != null)
                    instrumentation.Closed();
            }
            obj.Dispose();
        }

    }
}
